package cc.shacocloud.mirage.utils.converter.support;

import cc.shacocloud.mirage.utils.ClassUtil;
import cc.shacocloud.mirage.utils.charSequence.StrUtil;
import cc.shacocloud.mirage.utils.converter.ConversionException;
import cc.shacocloud.mirage.utils.converter.ConversionSupport;
import cc.shacocloud.mirage.utils.converter.TypeDescriptor;
import cc.shacocloud.mirage.utils.date.DateTime;
import cc.shacocloud.mirage.utils.date.DateUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.time.*;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;

/**
 * JDK8中新加入的java.time包对象解析转换器<br>
 * 支持的对象包括：
 * <pre>
 * java.time.Instant
 * java.time.LocalDateTime
 * java.time.LocalDate
 * java.time.LocalTime
 * java.time.ZonedDateTime
 * java.time.OffsetDateTime
 * java.time.OffsetTime
 * </pre>
 *
 * @author 思追(shaco)
 */
public class ToTemporalConversion extends AbstractConversion implements ConversionSupport {
    
    @Override
    public boolean support(@NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) {
        Class<?> objectType = targetType.getObjectType();
        return ClassUtil.isAssignable(Temporal.class, objectType);
    }
    
    @Override
    public Object convert(@NotNull Object source, @NotNull TypeDescriptor sourceType, @NotNull TypeDescriptor targetType) throws ConversionException {
        if (source instanceof Temporal) {
            return source;
        }
        Class<?> objectType = targetType.getObjectType();
        
        Object result;
        
        if (source instanceof Long) {
            result = parseFromLong((Long) source, objectType);
        } else if (source instanceof TemporalAccessor) {
            result = parseFromTemporalAccessor((TemporalAccessor) source, objectType);
        } else if (source instanceof Date) {
            DateTime dateTime = DateTime.of((Date) source);
            result = parseFromInstant(dateTime.toInstant(), dateTime.getZoneId(), objectType);
        } else if (source instanceof Calendar) {
            Calendar calendar = (Calendar) source;
            result = parseFromInstant(calendar.toInstant(), calendar.getTimeZone().toZoneId(), objectType);
        } else {
            result = parseFromCharSequence(convertToStr(source), objectType);
        }
        
        if (Objects.isNull(result)) {
            throw new ConversionException(sourceType, targetType, source);
        }
        
        return result;
    }
    
    /**
     * 转为 {@link Temporal}
     *
     * @param value 字符串值
     * @return 日期对象
     */
    @Nullable
    private Temporal parseFromCharSequence(CharSequence value,
                                           Class<?> targetType) {
        if (StrUtil.isBlank(value)) {
            return null;
        }
        
        DateTime dateTime = DateUtil.parse(value);
        Instant instant = Objects.requireNonNull(dateTime).toInstant();
        ZoneId zoneId = dateTime.getZoneId();
        return parseFromInstant(instant, zoneId, targetType);
    }
    
    /**
     * 转为 {@link Temporal}
     *
     * @param time 时间戳
     * @return java.time中的对象
     */
    private Temporal parseFromLong(Long time,
                                   Class<?> targetType) {
        return parseFromInstant(Instant.ofEpochMilli(time), null, targetType);
    }
    
    /**
     * 转为 {@link Temporal}
     *
     * @param temporalAccessor TemporalAccessor对象
     * @return java.time中的对象
     */
    private Temporal parseFromTemporalAccessor(TemporalAccessor temporalAccessor,
                                               Class<?> targetType) {
        Temporal result = null;
        if (temporalAccessor instanceof LocalDateTime) {
            result = parseFromLocalDateTime((LocalDateTime) temporalAccessor, targetType);
        } else if (temporalAccessor instanceof ZonedDateTime) {
            result = parseFromZonedDateTime((ZonedDateTime) temporalAccessor, targetType);
        }
        
        if (null == result) {
            result = parseFromInstant(DateUtil.toInstant(temporalAccessor), null, targetType);
        }
        
        return result;
    }
    
    /**
     * 转为 {@link Temporal}
     *
     * @param localDateTime {@link LocalDateTime}对象
     * @return java.time中的对象
     */
    @Nullable
    private Temporal parseFromLocalDateTime(LocalDateTime localDateTime,
                                            Class<?> targetType) {
        if (Instant.class.equals(targetType)) {
            return DateUtil.toInstant(localDateTime);
        }
        if (LocalDate.class.equals(targetType)) {
            return localDateTime.toLocalDate();
        }
        if (LocalTime.class.equals(targetType)) {
            return localDateTime.toLocalTime();
        }
        if (ZonedDateTime.class.equals(targetType)) {
            return localDateTime.atZone(ZoneId.systemDefault());
        }
        if (OffsetDateTime.class.equals(targetType)) {
            return localDateTime.atZone(ZoneId.systemDefault()).toOffsetDateTime();
        }
        if (OffsetTime.class.equals(targetType)) {
            return localDateTime.atZone(ZoneId.systemDefault()).toOffsetDateTime().toOffsetTime();
        }
        
        return null;
    }
    
    /**
     * 转为 {@link Temporal}
     *
     * @param zonedDateTime {@link ZonedDateTime}对象
     * @return java.time中的对象
     */
    @Nullable
    private Temporal parseFromZonedDateTime(ZonedDateTime zonedDateTime,
                                            Class<?> targetType) {
        if (Instant.class.equals(targetType)) {
            return DateUtil.toInstant(zonedDateTime);
        }
        if (LocalDateTime.class.equals(targetType)) {
            return zonedDateTime.toLocalDateTime();
        }
        if (LocalDate.class.equals(targetType)) {
            return zonedDateTime.toLocalDate();
        }
        if (LocalTime.class.equals(targetType)) {
            return zonedDateTime.toLocalTime();
        }
        if (OffsetDateTime.class.equals(targetType)) {
            return zonedDateTime.toOffsetDateTime();
        }
        if (OffsetTime.class.equals(targetType)) {
            return zonedDateTime.toOffsetDateTime().toOffsetTime();
        }
        
        return null;
    }
    
    /**
     * 转为 {@link Temporal}
     *
     * @param instant {@link Instant}对象
     * @param zoneId  时区ID，null表示当前系统默认的时区
     * @return java.time中的对象
     */
    private Temporal parseFromInstant(Instant instant,
                                      ZoneId zoneId,
                                      Class<?> targetType) {
        if (Instant.class.equals(targetType)) {
            return instant;
        }
        
        zoneId = Optional.ofNullable(zoneId).orElseGet(ZoneId::systemDefault);
        
        Temporal result = null;
        if (LocalDateTime.class.equals(targetType)) {
            result = LocalDateTime.ofInstant(instant, zoneId);
        } else if (LocalDate.class.equals(targetType)) {
            result = instant.atZone(zoneId).toLocalDate();
        } else if (LocalTime.class.equals(targetType)) {
            result = instant.atZone(zoneId).toLocalTime();
        } else if (ZonedDateTime.class.equals(targetType)) {
            result = instant.atZone(zoneId);
        } else if (OffsetDateTime.class.equals(targetType)) {
            result = OffsetDateTime.ofInstant(instant, zoneId);
        } else if (OffsetTime.class.equals(targetType)) {
            result = OffsetTime.ofInstant(instant, zoneId);
        }
        return result;
    }
    
}
